﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;
using ScheduledXAudio2.Structures;
using SharpDX;
using SharpDX.Multimedia;
using SharpDX.XAudio2;

namespace ScheduledXAudio2
{
    [StructLayout(LayoutKind.Sequential)]
    public struct PatchInfo
    {
        public int PatchSampleRate { get; set; }
        public int PatchAvgBytesPerSec { get; set; }
        public int PatchBitsPerSample { get; set; }
        public int PatchBlockAlign { get; set; }
        public int PatchChannels { get; set; }
        public WaveFormatEncoding PatchWaveFormatEncoding { get; set; }
        public int PatchExtraBytes { get; set; }
        public long PatchSize { get; set; }
    }

    public class DrumPatchLoader : INotifyPropertyChanged
    {
        static bool isLoaderThread { get; set; }
        public delegate void loadDelegate(SimpleUnit unit, string name);
        public static EventHandler Loading;
        public static EventHandler DoneLoading;
        public static EventHandler LoadingError;
        private static SimpleUnit currentunit;
        private static void LoadingEvent()
        {
            if (Loading != null)
            {
                Loading(Instance, new EventArgs());
                IsLoading = true;
                isLoaderThread = true;
            }
        }
        private static void DoneLoadingEvent()
        {
            if (DoneLoading != null)
            {
                DoneLoading(Instance, new EventArgs());
                IsLoading = false;
                isLoaderThread = false;
            }
        }
        private static void LoadingErrorEvent()
        {
            if (LoadingError != null)
            {
                LoadingError(Instance, new EventArgs());
                DoneLoadingEvent();
            }
        }
        #region Singleton Pattern
        private static DrumPatchLoader instance;
        public static DrumPatchLoader Instance
        {
            get
            {
                if (instance == null)
                    instance = new DrumPatchLoader();
                return instance;
            }
        }
        #endregion Singleton Pattern
        public static PatchInfo LastLoadedPatchInfo
        {
            get;
            protected set;
        }
        public static int lastinfo
        {
            get;
            set;
        }
        public static bool IsLoading
        {
            get;
            protected set;
        }
        private static Object StaticLock = new Object();
        /// <summary>
        /// Loading and Unloading event put in place in attempt to avoid "file in use" exceptions.
        /// the exception is still raised if LoadWav() is spammed too many times too quickly.
        /// </summary>
        /// <param name="unit"></param>
        /// <param name="soundName"></param>
        /// <returns></returns>
        internal static bool LoadWAV(out SimpleUnit unit, string soundName)
        {
            if (File.Exists(soundName))
            {
                lock (StaticLock)
                {
                    System.Threading.ParameterizedThreadStart pthread = new System.Threading.ParameterizedThreadStart(LoadWavthreadRun);
                    System.Threading.Thread thread = new System.Threading.Thread(pthread);
                    thread.Start(soundName);
                    while (currentunit == null)
                    {

                    }
                    unit = currentunit;
                    currentunit = null;
                    return true;
                }
            }
            LoadingErrorEvent();
            unit = null;
            return false;
        }
        internal static void LoadWavthreadRun(object soundName)
        {
            LoadingEvent();
            currentunit = null;
            var stream = new SampleStream(File.Open(soundName.ToString(), FileMode.Open));
            WaveFormat format = stream.Format;

            AudioBuffer buffer = new AudioBuffer
            {
                Stream = stream,
                AudioBytes = (int)stream.Length,
                Flags = BufferFlags.EndOfStream
            };
            VoiceSendDescriptor vsd = new VoiceSendDescriptor();
            LastLoadedPatchInfo = new PatchInfo()
            {
                PatchSampleRate = format.SampleRate,
                PatchAvgBytesPerSec = format.AverageBytesPerSecond,
                PatchBitsPerSample = format.BitsPerSample,
                PatchBlockAlign = format.BlockAlign,
                PatchChannels = format.Channels,
                PatchWaveFormatEncoding = format.Encoding,
                PatchExtraBytes = format.ExtraSize,
                PatchSize = stream.Length
            };
            currentunit = new SimpleUnit(buffer, vsd, format);
            while (currentunit != null)
            {

            }
            DoneLoadingEvent();
        }
        internal static void LoadStreamthreadRun(object _stream)
        {
            LoadingEvent();
            currentunit = null;
            //var stream = new SampleStream(File.Open(soundName.ToString(), FileMode.Open));
            var stream = _stream as SampleStream;
            WaveFormat format = stream.Format;

            AudioBuffer buffer = new AudioBuffer
            {
                Stream = stream,
                AudioBytes = (int)stream.Length,
                Flags = BufferFlags.EndOfStream
            };
            VoiceSendDescriptor vsd = new VoiceSendDescriptor();

            LastLoadedPatchInfo = new PatchInfo()
            {
                PatchSampleRate = format.SampleRate,
                PatchAvgBytesPerSec = format.AverageBytesPerSecond,
                PatchBitsPerSample = format.BitsPerSample,
                PatchBlockAlign = format.BlockAlign,
                PatchChannels = format.Channels,
                PatchWaveFormatEncoding = format.Encoding,
                PatchExtraBytes = format.ExtraSize,
                PatchSize = stream.Length
            };
            currentunit = new SimpleUnit(buffer, vsd, format);
            while (currentunit != null)
            {

            }
            DoneLoadingEvent();
        }
        internal static void UnloadWav(SimpleUnit unit)
        {
            unit.Format = null;
            unit.Vsd.OutputVoice.Dispose();
            unit.Buffer.Stream.Dispose();
        }
        internal static bool LoadStream(out SimpleUnit unit, Stream stream)
        {
            if (stream != null)
            {
                lock (StaticLock)
                {
                    SampleStream s = new SampleStream(stream);
                    System.Threading.ParameterizedThreadStart pthread = new System.Threading.ParameterizedThreadStart(LoadStreamthreadRun);
                    System.Threading.Thread thread = new System.Threading.Thread(pthread);
                    thread.Start(s);
                    while (currentunit == null)
                    {

                    }
                    unit = currentunit;
                    currentunit = null;
                    return true;
                }
            }
            unit = null;
            LoadingErrorEvent();
            return false;
        }
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion INotifyPropertyChanged
    }
}
